package de.mmis.core.base.abstracttree;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import de.mmis.core.base.abstracttree.LeafNode.Encoding;

/**
 * represents an abstract Tree that is a list of simpler abstract Trees
 * 
 * @author Christian Koch
 */
public class InnerNode extends Tree implements Iterable<Tree> {
	private ArrayList<Tree> subTreeList;

	/**
	 * creates new Struct without any abstract Trees in its list
	 */
	public InnerNode() {
		this.subTreeList = new ArrayList<Tree>();
	}

	/**
	 * creates new Struct with given abstract Tree list
	 * 
	 * @param exps
	 *            abstract Trees to add to Struct
	 */
	public InnerNode(Tree... exps) {
		this.subTreeList = new ArrayList<Tree>();
		for (Tree exp : exps)
			add(exp);
	}

	/**
	 * creates new Struct with given abstract Tree list
	 * 
	 * @param exps
	 *            abstract Trees to add to Struct
	 */
	public InnerNode(List<Tree> exps) {
		this.subTreeList = new ArrayList<Tree>();
		for (Tree exp : exps)
			add(exp);
	}

	/**
	 * adds new abstract Tree to list
	 * 
	 * @param exp
	 *            abstract Tree to add
	 */
	public void add(Tree exp) {
		this.subTreeList.add(exp);
	}

	/**
	 * @return number of abstract Trees currently in list
	 */
	public int getNumberOfSubTrees() {
		return this.subTreeList.size();
	}

	/**
	 * @param index
	 *            index of abstract Tree to be returned
	 * @return abstract Tree on given index position in list
	 */
	public Tree getSubTree(int index) {
		return this.subTreeList.get(index);
	}

	/**
	 * sets sub-Tree to new value
	 * 
	 * @param index
	 *            index of sub-Tree to be changed
	 * @param exp
	 *            new content
	 */
	public void setSubTree(int index, Tree exp) {
		this.subTreeList.set(index, exp);
	}

	/**
	 * @return type of this Tree instance, which is always Type.Struct for
	 *         Structs
	 */
	@Override
	public Type getType() {
		return Type.INNER;
	}

	/**
	 * removes all base64 container abstract Tree wrappers from this abstract
	 * Tree
	 * 
	 * @param dataCopy
	 *            true, if Atom abstract Trees should also been cloned
	 * @return abstract Tree with same structure and content as current one and
	 *         not including any base64 container
	 */
	@Override
	public Tree copy(boolean dataCopy) {
		InnerNode ret = new InnerNode();
		for (int i = 0; i < this.subTreeList.size(); i++)
			ret.add(this.subTreeList.get(i).copy(dataCopy));
		return ret;
	}

	@Override
	public int hashCode() {
		int hash = 0;
		for (Tree exp : this.subTreeList)
			hash += exp.hashCode();
		return hash;
	}

	@Override
	protected boolean equals(Tree e, boolean wildcards) {
		if (wildcards && e.getType() == Type.LEAF
				&& ((LeafNode) e).getEncoding() == Encoding.Token
				&& ((LeafNode) e).equalsUTF8String("?"))
			return true;

		if (e.getType() != Type.INNER
				|| ((InnerNode) e).getNumberOfSubTrees() != this
						.getNumberOfSubTrees())
			return false;
		for (int i = 0; i < this.getNumberOfSubTrees(); i++)
			if (!this.getSubTree(i).equals(((InnerNode) e).getSubTree(i),
					wildcards))
				return false;
		return true;
	}

	@Override
	public Iterator<Tree> iterator() {
		return this.subTreeList.iterator();
	}
}
